package src;

//import java.awt.List;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;

public class Con {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub

		/*
		 //ThreadLocalVariable Demo
		Runnable r = new ThreadLocalTest();
		Thread t1 = new Thread(r);
		Thread t2 = new Thread(r);
		t1.start();
		t1.join();
		t2.start();
		 */

		/*	//DeadLock Demo
		DeadLockDemo r1 = new DeadLockDemo();
		DeadLockDemo r2 = new DeadLockDemo();
		Thread t1 = new Thread(r1);
		Thread t2 = new Thread(r2);
		r1.setThreadRes(t2);
		r2.setThreadRes(t1);
		t1.start();
		t2.start();
		Thread.currentThread().sleep(5000);
		t1.notify();
		 */

		/*
		//Code for Locking and unlocking
		Runnable r = new LockDemo();
		Thread t1 = new Thread(r);
		Thread t2 = new Thread(r);
		Thread t3 = new Thread(r);
		t1.start();
		Thread.currentThread().sleep(1000);
		t2.start();
		Thread.currentThread().sleep(1000);
		t3.start();
		 */

		/*
		//ReadWriteLock Demo
		ReadWriteLockDemo rwl = new ReadWriteLockDemo();
		Reader rThread = new Reader(rwl);
		Writer wThread = new Writer(rwl);
		rThread.start();
		wThread.start();
		 */
		/*	
		//Semaphore tests
		SemaphoreTest st = new SemaphoreTest();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		t1.start();
		t2.start();
		 */

		/*
		//Blocking queue test
		BlockingQueue<Integer> bq = new ArrayBlockingQueue<Integer>(5);
		Thread t1= new Thread(new ContentConsumer(bq));
		Thread t2 = new Thread(new ContentProducer(bq));
		t1.start();
		t2.start();
		 */

		/*
		//AtomicInteger example - replacement for sync and volatile methods
		AtomicInteger ai = new AtomicInteger();
		Thread t1 = new Thread(new AtomicExample(ai));
		t1.start();
		 */
		/*
		//ThreadPoolExecutor example
		ExecutorService es = Executors.newFixedThreadPool(5);
		for(int i = 0; i< 10;i++){
			es.execute(new WorkerThread());
		}
		es.shutdown();
		while(!es.isTerminated()){
			Thread.currentThread().sleep(1000);
		}
		System.out.println("ALL THREADS SHUTDOWN");
	//	List<Object> l = new ArrayList<String>();
	 * */
		
	 
		
	}

}


class WorkerThread implements Runnable{

	@Override
	public void run() {
			System.out.println(Thread.currentThread().getName()+" Executing");
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+" Finished");
	}
	
}

class AtomicExample implements Runnable{
	public AtomicInteger ai;
	public AtomicExample(AtomicInteger ai){
		this.ai = ai;
	}

	@Override
	public void run() {
		while(true){
		ai.set(5);//writes Atomically
		System.out.println(ai.get());//reads Atomically
		try {
			Thread.currentThread().sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		}
	}

}


class ContentProducer implements Runnable{
	BlockingQueue<Integer> bq ;
	private Integer cnt = 0;
	public ContentProducer(BlockingQueue<Integer> bq ){
		this.bq = bq;
	}

	@Override
	public void run() {
		while(true){
			cnt++;
			try {
				bq.put(cnt);
				System.out.println(Thread.currentThread().getName()+" Produced: "+cnt);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}

}

class ContentConsumer implements Runnable{
	BlockingQueue<Integer> bq ;
	private Integer cnt = 0;
	public ContentConsumer(BlockingQueue<Integer> bq ){
		this.bq = bq;
	}

	@Override
	public void run() {
		while(true){
			Integer cnt;
			try {
				cnt = bq.take();
				System.out.println(Thread.currentThread().getName()+" Consumed: "+cnt);
				Thread.currentThread().sleep(3000);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}

	}

}

class SemaphoreTest implements Runnable{
	Semaphore s = new Semaphore(1);//single permit semaphore
	@Override
	public void run() {
		try {
			s.acquire();
			System.out.println(Thread.currentThread().getName()+" - Entered Semaphore");
			Thread.currentThread().sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			s.release();
		}

	}

}

class ReadWriteLockDemo{
	private ReadWriteLock rwl = new ReentrantReadWriteLock(true);
	private Lock rl = rwl.readLock();
	private Lock wl = rwl.writeLock();
	private static int counter;

	public int readCount(){
		try{
			rl.lock();
			return counter;
		}finally{
			rl.unlock();
		}
	}

	public int incrementCount(){
		wl.lock();
		try {
			Thread.currentThread().sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		counter++;
		wl.unlock();
		return counter;
	}

}

class Reader extends Thread{
	private ReadWriteLockDemo rw;
	public Reader(ReadWriteLockDemo rw){
		this.rw = rw;
	}

	public void run(){
		while(true){
			System.out.println(Thread.currentThread().getName()+": rw.readCount(): "+rw.readCount());
		}
	}

}

class Writer extends Thread{
	private ReadWriteLockDemo rw;
	public Writer(ReadWriteLockDemo rw){
		this.rw = rw;
	}

	public void run(){
		while(true){
			int cnt = rw.incrementCount();
			System.out.println(Thread.currentThread().getName()+":  rw.incrementCount(): "+cnt);
			if(cnt>10){
				break;
			}
		}
	}

}

class LockDemo implements Runnable{
	private Lock lock = new ReentrantLock(true);//true for fair locking policy
	@Override
	public void run() {

		System.out.println(Thread.currentThread().getName()+" Waiting for lock");
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName()+" Acquired Lock");
			Thread.currentThread().sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			System.out.println(Thread.currentThread().getName()+" Releasing Lock");
			lock.unlock();
		}

		if(lock.tryLock()){
			System.out.println(Thread.currentThread().getName()+" Acquired Lock");
			lock.unlock();
			System.out.println(Thread.currentThread().getName()+" Released Lock");

		}


	}

}

class DeadLockDemo implements Runnable{
	private Thread t;
	public void setThreadRes(Thread t){
		this.t = t;
	}

	@Override
	public void run() {

		try {
			synchronized (t) {
				System.out.println("Before wait");
				t.wait();
				System.out.println("After wait");
			}


		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}


	}

}

class ThreadLocalTest implements Runnable{
	ThreadLocal<String> threadLocalV = new ThreadLocal<String>();
	String s;
	public void run(){
		System.out.println(Thread.currentThread().getName()+": threadLocalV.get(): "+threadLocalV.get()+" - s: "+s);
		threadLocalV.set("Hello");
		s = "Hello";
		System.out.println("Values set by: "+Thread.currentThread().getName());
	}
}
